﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reactive.Linq;
using System.Linq;

using alib;
using alib.Enumerable;
using alib.Debugging;
using alib.Collections.ReadOnly;
using alib.Observable;

namespace agree
{
	using transfer.old;

	public abstract partial class TfsBuilder : TdlParsePos
	{
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public unsafe class Xdl : TfsBuilder, IReadOnlyList<RootNodeData>
		{
			public static TfsBuilder.Xdl Build(IRtParent parent, TransferGrammar tg, IReadOnlyList<TransferRule.XdlRel> rels)
			{
				if (rels.Count == 0)
					throw new Exception();

				return new TfsBuilder.Xdl(parent, tg, rels);
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// 
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			Xdl(IRtParent parent, TransferGrammar tg, IReadOnlyList<TransferRule.XdlRel> rels)
				: base(parent, tg, tg[0].g.ftm.r_none)
			{
				this.rgrme = new RelModeEdge[rels.Count];
				for (int i = 0; i < rels.Count; i++)
				{
					TransferRule.XdlRel rel = rels[i];
					Debug.Print(rel.ToString());

					/// this is a bit hacky: set the TypeMgr for this iteration, so that it will not only be used
					/// for the tdl parse, but so that it gets captured into the TmFeatMarks that keep track of coreferencing
					____g = rel.trm.g;

					int c_pre = base.EdgeCount;
					Edge e = _do_one_tdl_rel(rel.tdl);
					rgrme[i].rnd = new RootNodeData(rel.trm.tgrm, e);
					rgrme[i].slot_count = base.EdgeCount - c_pre;
					rgrme[i].last_mark = base.next_mark - 1;

					____g = null;
				}

				this.c_act = base.EdgeCount;	// fix fix
				this.c_corefs = corefs.Count;

				//xmts = this.xmaptags;

				_debug_info();
			}

			//Dictionary<string, XMapTag> xmts

			public RelModeEdge[] rgrme;

			public struct RelModeEdge
			{
				/// <summary> grammar, 'in-rels' or 'out-rels', and root edge (and 'first_mark') </summary>
				public RootNodeData rnd;
				/// <summary> 'range-inclusive' (meaning that this mark is not necessarily used, but if it is, then it is used by this rel) </summary>
				public int last_mark;
				/// <summary> Does not include a slot for the root </summary>
				public int slot_count;
			};

			public override void GetIndexData(int ix1, arr_tfs_entry* pate)
			{
				Debug.Assert(ix1 > 0);

				int ix = ix1 - 1;
				if (ix < c_act)
				{
					*pate = entries[ix];
					if (pate->hash_code == -1)
					{
						*pate = default(arr_tfs_entry);
						return;
					}
					for (ix = 0; ix < rgrme.Length; ix++)
						if (pate->mark <= rgrme[ix].last_mark)
							goto ok;
					throw new Exception();
				}
				else
				{
					*pate = default(arr_tfs_entry);
					ix -= (int)c_act;
				}
			/// don't set pate->rnd, it would change the edge to the root edge
			ok:
				pate->tgrm = rgrme[ix].rnd.tgrm;
				pate->next = 0;
			}

			public override int SlotCount
			{
				get { return base.SlotCount + rgrme.Length; }
			}

			protected override void _copy_dict(arr_tfs_entry* dst, arr_tfs_entry* src, int c)
			{
				while (c > 0)
				{
					if (src->hash_code != ListTerm)
					{
						*dst = *src;

						for (int ix = 0; ix < rgrme.Length; ix++)
							if (src->mark <= rgrme[ix].last_mark)
							{
								dst->tgrm = rgrme[ix].rnd.tgrm;
								goto ok;
							}
						throw new Exception();
					ok:
						dst++;
						c--;
					}
					src++;
				}
			}

#if REVERSE_INDEX
			protected override ReadOnlyDictionary<int, int[]> _rebuild_reverse_index()
			{
				Range r = this.Values.Range(e => e.Mark);
				this.Keys.ExtendRange(ref r, fm => fm.m);
				base.c_corefs = r.Max + 1;

				List<int>[] L = new List<int>[r.Extent];

				int ix = 0;
				for (ix = 0; ix < base.SlotCount; ix++)
				{
					if (entries[ix].hash_code != -1)
					{
						int om = entries[ix].e_Mark;
						if (om != 0)
							(L[om - r.Min] ?? (L[om - r.Min] = new List<int>())).Add(ix + 1);
					}
				}

				for (int i = 0; i < rgrme.Length; i++, ix++)
				{
					int om = rgrme[i].rnd.e.Mark;
					if (om != 0)
						(L[om - r.Min] ?? (L[om - r.Min] = new List<int>())).Add(ix + 1);
				}

				var z = L.TagIndex()
						.Where(a => a.Item != null)
						.Select(a => new KeyValuePair<int, int[]>(a.Index + r.Min, alib.Enumerable._enumerable_ext._ToArray(a.Item)))
						.ToReadOnlyDictionary();

				return z;
			}

			public override Edge GetCorefEdge(int out_mark)
			{
				int[] ret;
				if (!_reverse_index.TryGetValue(out_mark, out ret))
					return default(Edge);

				return entries[ret[0] - 1].e;
			}
#endif

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// 
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			Edge _do_one_tdl_rel(agree.Tdl s_tdl)
			{
				var tdlt = new TdlTokenizer(parent, s_tdl);
				var tgrp = new TdlConstraintGrouper(parent, tdlt, TdlType.TransferRule);

				tgrp.ConstraintBodyOnly = true;

				var tokg = Observable.Wait(tgrp).constraints;


				Type t = im.td.ΔTop;
				foreach (TdlTokenList tdl_expr in tokg)
				{
					Type tcur = im.ftm[tdl_expr[0].Text].introduced_by;
					if (tcur == t || tcur == null)
						continue;
					Debug.Assert(!tcur.IsTop);
					if (t == null)
						t = tcur;
					else if ((t = t.tu.UnifyTypesFull(t, tcur)) == null)
						throw new TdlException("inferred types for root features in rel do not unify.");
				}
				Debug.Assert(t != null && t.HasAppropriateFeatures);

				Edge e = CreateEdge(t, false);
				foreach (TdlTokenList tdl_expr in tokg)
					_accept_base_feature_constraint(e, tdl_expr);
				return e;
			}

			public new RootNodeData this[int index] { get { return rgrme[index].rnd; } }

			public int Count { get { return rgrme.Length; } }

			public IEnumerator<RootNodeData> GetEnumerator()
			{
				for (int i = 0; i < rgrme.Length; i++)
					yield return rgrme[i].rnd;
			}

			System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
			{
				return GetEnumerator();
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// 
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			[Conditional("DEBUG")]
			public void _debug_info()
			{
				ICollection<FeatMark> fms = Keys;
				ICollection<Edge> edx = Values;
				ICollection<TfsBuilder.Coreference> ic_corefs = corefs.Values;

				Debug.Print("rel-roots:               {0}", rgrme.Length);
				Debug.Print("non-root-slots:          {0}", EdgeCount);
				Debug.Print("nodes:                   {0}", Values.CountDistinct());
				Debug.Print("corefs:                  {0}", base.EdgeCount);
				Debug.Print("cross-check:             {0}", _fme_enum().GroupBy(e => e.Value, e => e.Key).Count(grp => grp._Count() > 1));
				Debug.Print("featmarks-distinct:      {0}", fms.IsDistinct());
				Debug.Print("coref-featmarks-distinct:{0}", ic_corefs.SelectMany().IsDistinct());
				Debug.Print("coref-featmarks          {0}", ic_corefs.Sum(x => x.Count));
				Debug.Print("coref-fms-all-present:   {0}", ic_corefs.SelectMany().All(tfm => ContainsKey(tfm.fm)));
				Debug.Print("out-tuple-equiv: (==0?)  {0}", Values.GroupBy(e => e).Count(grp => grp._Count() > 1));
				Debug.Print("in-mark range:          [{0}-{1}]", fms.Min(fm => fm.m), fms.Max(fm => fm.m));
				Debug.Print("in-marks all!=0          {0}", fms.None(fm => fm.m == 0));
				Debug.Print("in-marks < 0:            {0}", fms.Count(fm => fm.m < 0));
				Debug.Print("in-marks > 0:            {0}", fms.Count(fm => fm.m > 0));
				Debug.Print("out-mark range:         [{0}-{1}]", Values.Min(e => e.Mark), Values.Max(e => e.Mark));
				Debug.Print("in-marks < 0:            {0}", fms.Count(fm => fm.m < 0));
				Debug.Print("in-marks > 0:            {0}", fms.Count(fm => fm.m > 0));
				Debug.Print("");
				for (int i = 0; i < rgrme.Length; i++)
				{
					RelModeEdge rme = rgrme[i];
					RootNodeData rnd = rme.rnd;
					var sh = alib.String._string_ext.CondenseSpaces(String.Format("------ rel root #{0} {1} tag:{2}", i, rnd.ToString(), rnd.tgrm.Tag));
					Debug.Print(sh);
					Debug.Print("root-edge:         {0,-20} {1:X8}", rnd.e.ToString(im.ftm, true), (int)rnd.e.FlagsId);
					Debug.Print("last-mark:         {0}", rme.last_mark);
					Debug.Print("slots:             {0}", rme.slot_count);
					int j = 0;
					foreach (var kvp in _fme_enum())
					{
						int m = kvp.Key.m;
						if (rnd.e.Mark <= m && m <= rme.last_mark)
						{
							Debug.Print("{0,4} {1:X8} {2,15} {3}",
								"[" + j + "]",
								(int)kvp.Value.FlagsId,
								kvp.Key.ShortDisplay(ftm),
								kvp.Value.ToString(im.ftm, true));
						}
						j++;
					}
				}
				Debug.Print("");
			}
		};
	};
}